﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Dracham.MapEditor.Textures;
using System.Xml;
using Dracham.MapEditor.Singleton;
using Dracham.MapEditor.Zones.Events;
using System.Reflection;
using Dracham.MapEditor.Zones.Extras;

namespace Dracham.MapEditor.Zones
{
    public class ZoneTile
    {
        public Tile Background { get; set; }
        public Tile Decoration { get; set; }
        public Boolean Solid { get; set; }
        public Int32 X { get; private set; }
        public Int32 Y { get; private set; }

        public List<Event> Events { get; set; }

        //
        // TODO:
        // build better!!!
        public SpawnPoint SpawnPoint { get; set; }

        public ZoneTile()
        {
            this.Events = new List<Event>();
        }

        public static ZoneTile FromXmlNode(XmlNode node)
        {
            ZoneTile zt = new ZoneTile();

            if (node.Attributes["BackgroundTexture"] != null)
            {
                String backgroundTexture = node.Attributes["BackgroundTexture"].Value;
                Int32 backgroundTextureX = Convert.ToInt32(node.Attributes["BackgroundTextureX"].Value);
                Int32 backgroundTextureY = Convert.ToInt32(node.Attributes["BackgroundTextureY"].Value);
                zt.Background = DrachamMapEditor.TextureManager.GetTexture(backgroundTexture).GetTile(backgroundTextureX, backgroundTextureY);
            }

            if (node.Attributes["DecorationTexture"] != null)
            {
                String decorationTexture = node.Attributes["DecorationTexture"].Value;
                Int32 decorationTextureX = Convert.ToInt32(node.Attributes["DecorationTextureX"].Value);
                Int32 decorationTextureY = Convert.ToInt32(node.Attributes["DecorationTextureY"].Value);

                zt.Decoration = DrachamMapEditor.TextureManager.GetTexture(decorationTexture).GetTile(decorationTextureX, decorationTextureY);
            }

            zt.X = Convert.ToInt32(node.Attributes["X"].Value);
            zt.Y = Convert.ToInt32(node.Attributes["Y"].Value);

            if (node.Attributes["Solid"] != null)
            {
                zt.Solid = Boolean.Parse(node.Attributes["Solid"].Value);
            }
            else
            {
                zt.Solid = false;
            }

            //
            // parse events
            if (node.HasChildNodes)
            {
                //
                // get event assembly
                Assembly a = typeof(Event).Assembly;
                String typeFullNamePrefix = typeof(Event).FullName;
                typeFullNamePrefix = typeFullNamePrefix.Substring(0, typeFullNamePrefix.LastIndexOf(".") + 1);

                foreach (XmlNode child in node.ChildNodes)
                {
                    //
                    // search for the event type
                    Type t = a.GetType(typeFullNamePrefix + child.Name);

                    //
                    // check whatever the type was found and if it's a valid Event
                    if (t != null && t.BaseType == typeof(Event))
                    {
                        //
                        // get a method from the type
                        MethodInfo mi = t.GetMethod("FromXmlNode", BindingFlags.Static | BindingFlags.Public);

                        //
                        // check whatever the method was found
                        if (mi != null)
                        {
                            //
                            // run the method and add the event...
                            zt.Events.Add((Event)mi.Invoke(null, new Object[] { child }));
                        }
                    }
                    else if (child.Name == "Spawn")
                    {
                        zt.SpawnPoint = new SpawnPoint(child);
                    }
                }
            }

            return zt;
        }

        public XmlNode ToXmlNode(Int32 x, Int32 y)
        {
            XmlNode node = DrachamMapEditor.ZoneManager.CreateElement("ZoneTile");

            if (Background != null)
            {
                node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("BackgroundTexture", Background.Texture.Name));
                node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("BackgroundTextureX", Background.X.ToString()));
                node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("BackgroundTextureY", Background.Y.ToString()));
            }

            if (Decoration != null)
            {
                node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("DecorationTexture", Decoration.Texture.Name));
                node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("DecorationTextureX", Decoration.X.ToString()));
                node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("DecorationTextureY", Decoration.Y.ToString()));
            }

            this.X = x;
            this.Y = y;

            node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("X", x.ToString()));
            node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("Y", y.ToString()));
            node.Attributes.Append(DrachamMapEditor.ZoneManager.CreateAttribute("Solid", Solid.ToString()));

            foreach (Event e in this.Events)
            {
                node.AppendChild(e.ToXmlNode());
            }

            if (SpawnPoint != null)
            {
                node.AppendChild(SpawnPoint.ToXmlNode());
            }

            return node;
        }

        public T GetOrCreateEvent<T>(Type type)
        {
            Object e = this.Events.Where(t => t.GetType() == type).SingleOrDefault();

            if (e == null)
            {
                e = type.Assembly.CreateInstance(type.FullName);

                this.Events.Add((Event)e);
            }

            return (T)e;
        }
    }
}
